package com.ficus.road.maintain.service;

import com.ficus.road.maintain.core.exception.BadRequestException;
import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.core.model.config.GlobalConfig;
import com.ficus.road.maintain.core.model.coverage.RoadCoverageWeek;
import com.ficus.road.maintain.core.model.coverage.UnitCoverageDay;
import com.ficus.road.maintain.core.model.coverage.UnitCoverageWeek;
import com.ficus.road.maintain.core.model.density.UnitDensity;
import com.ficus.road.maintain.core.model.zh.UnitScore;
import com.ficus.road.maintain.dao.*;
import com.ficus.road.maintain.dao.coverage.RoadCoverageWeekDao;
import com.ficus.road.maintain.dao.coverage.UnitCoverageDayDao;
import com.ficus.road.maintain.dao.coverage.UnitCoverageWeekDao;
import com.ficus.road.maintain.dao.density.UnitDensityDao;
import com.ficus.road.maintain.util.BaiduMapClient;
import com.ficus.road.maintain.util.MyDateUtil;
import com.google.common.base.Stopwatch;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author xiawen yang
 * @date 2021/11/4 下午2:57
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CompanyService {

    private final SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
    private final CarCoverageDao carCoverageDao;
    /**
     * 关键点位以及它的邻居节点
     */
    @Getter
    private final Map<String, Map<String, Set<String>>> companyKeyPointsMap = new HashMap<>();
    private final CompanyDao companyDao;
    private final GpsKeyPointsDao gpsKeyPointsDao;
    private final GpsTestDao gpsTestDao;
    private final BaiduMapClient baiduMapClient;
    private final UnitDao unitDao;
    private final RoadCoverageDensityDao roadCoverageDensityDao;
    private final RoadInfoDao roadInfoDao;
    private final UnitCoverageWeekDao unitCoverageWeekDao;
    private final RoadCoverageWeekDao roadCoverageWeekDao;
    private final UnitDensityDao unitDensityDao;
    private final UnitCoverageDayDao unitCoverageDayDao;
    private final CycleNumService cycleNumService;
    private final UnitScoreDao unitScoreDao;
    private final ShowRoadIntactDao showRoadIntactDao;
    private final GlobalConfigDao globalConfigDao;
    /**
     * <<覆盖率轮次id>, <公司id, 标段道路覆盖详情信息>>
     */
    @Getter
    private final Cache<String, Map<String, List<CompanyCoverRateInfoResponse>>> cycleNumCompanyCoverageMap = CacheBuilder.newBuilder().maximumSize(10).build();
    /**
     * <覆盖率轮次Id, <标段Id, 覆盖率信息>>
     */
    @Getter
    private final Map<String, Map<String, UnitCoverRateResponse>> monthlyUnitCoverageMap = new ConcurrentHashMap<>();
    private Map<String, CoverRateAll> coverRateMap = new HashMap<>();

    /**
     *  道路巡检率 - 根据标段id 进行过滤 -- 其中城市道路对应的过滤表
     *  数据来源 -- global config
     */
    private List<String> roadCoverUnitIdConfigForRoad = new ArrayList<>();

    /**
     *  道路巡检率 - 根据标段id 进行过滤 -- 其中高速公路对应的过滤表
     *  数据来源 -- jingyan的表
     */
    private List<String> roadCoverUnitIdConfigForHighway = new ArrayList<>();

    @PostConstruct
    public void init() {

        List<Company> companyList = companyDao.findAll();


        List<GpsKeyPoint> allKeyPoints = gpsKeyPointsDao.findAll();
        Map<String, Set<String>> collect = allKeyPoints.stream().collect(Collectors.toMap(GpsKeyPoint::getId, item -> CollectionUtils.isNotEmpty(item.getNeighbors()) ? item.getNeighbors() : new HashSet<>()));

        companyList.forEach(company -> companyKeyPointsMap.put(company.getId(), CollectionUtils.isEmpty(company.getKeyPoints()) ? new HashMap<>() : company.getKeyPoints().stream().collect(Collectors.toMap(item -> item, item -> collect.getOrDefault(item + "_" + company.getId(), new HashSet<>())))));
//        refreshCoverRate();

//        refreshCompanyCoverRate();

//        refreshUnitCoverRate();
        initRoadCoverUnitIdConfig();
    }
    private void initRoadCoverUnitIdConfig() {
        initRoadCoverUnitIdConfigForRoad();
        initRoadCoverUnitIdConfigForHighway();
    }

    private void initRoadCoverUnitIdConfigForHighway() {
        List<ShowRoadIntact.Road> all = showRoadIntactDao.findAll();
        roadCoverUnitIdConfigForHighway.addAll(all.get(0).getAllRoad());
    }

    private void initRoadCoverUnitIdConfigForRoad() {
        GlobalConfig<List<String>> one = globalConfigDao.findOne(new Query(Criteria.where("config_name").is("aval_road_unit_id")));
        if (one!=null)
            roadCoverUnitIdConfigForRoad.addAll(one.getConfigValue());
    }

    //    @Scheduled(cron = "0/5 * * * * ?", zone = "GMT+8:00")
    @Scheduled(cron = "0 0/10 * * * ?", zone = "GMT+8:00")
    public void refreshUnitCoverRate() {
        log.info("更新道路巡检率弹窗");
        monthlyUnitCoverageMap.clear();
        Date today = new Date();
        String monthAgoCycleNum = cycleNumService.getMonthAgoCycleNum();
        List<Unit> unitList = unitDao.findAll();
        Map<String, Unit> unitIdMap = unitList.stream().collect(Collectors.toMap(Unit::getId, item -> item));
        Map<String, String> unitCompanyNameMap = new HashMap<>();
        List<Company> companyList = companyDao.findAll();
        companyList.forEach(item -> {
            for (String unit : item.getUnits()) {
                unitCompanyNameMap.put(unit, item.getName());
            }
        });

        List<UnitCoverageWeek> monthUnitCoverageList = unitCoverageWeekDao.findByQuery(new Query(Criteria.where("cycle_num").gte(monthAgoCycleNum)));
        Map<String, List<UnitCoverageWeek>> cycleNumCoverageMap = monthUnitCoverageList.stream().collect(Collectors.groupingBy(UnitCoverageWeek::getCycleNum));
        List<UnitCoverageDay> todayUnitCoverageList = unitCoverageDayDao.findByQuery(new Query(Criteria.where("date").is(MyDateUtil.dateFormatStr(today, "yyyy-MM-dd"))));
        cycleNumCoverageMap.forEach((cycleNum, unitCoverageList) -> {
            Map<String, UnitCoverRateResponse> cycleNumCoverageResponse = unitCoverageList.stream().map(item -> {
                UnitCoverRateResponse unitCoverRateResponse = new UnitCoverRateResponse();
                if (unitIdMap.containsKey(item.getUnitId())) {
                    Optional<UnitCoverageDay> first = todayUnitCoverageList.stream().filter(todayCoverage -> todayCoverage.getUnitId().equals(item.getUnitId())).findFirst();
                    String s = "0";
                    if (first.isPresent()) {
                        s = first.get().getDayCoverage() + "";
                    }
                    String todayRate = CollectionUtils.isEmpty(todayUnitCoverageList) ? "0" : s;
                    Unit unit = unitIdMap.get(item.getUnitId());
                    unitCoverRateResponse.setUnitId(item.getUnitId());
                    unitCoverRateResponse.setUnitName(unit.getUnitName());
                    unitCoverRateResponse.setTodayRate(todayRate);
                    unitCoverRateResponse.setRoadType(unit.getRoadType());
                    unitCoverRateResponse.setWeekRate(item.getCycleCoverage() + "");
                    String companyName = unitCompanyNameMap.getOrDefault(item.getUnitId(), null);
                    unitCoverRateResponse.setCompany(companyName);
                    return unitCoverRateResponse;
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toMap(UnitCoverRateResponse::getUnitId, item -> item, (entity1, entity2) -> entity1));
            monthlyUnitCoverageMap.put(cycleNum, cycleNumCoverageResponse);
        });
    }

    @Scheduled(cron = "0 0/5 * * * ?", zone = "GMT+8:00")
//    @Scheduled(cron = "0/5 * * * * ?", zone = "GMT+8:00")
    public void refreshCompanyCoverRate() {
        List<Company> all = companyDao.findAll();
        List<String> cycleNumList = cycleNumService.getMonthAgoCycleNumList();
        String currentCycleNum = cycleNumService.getCurrentCycleNum();
//        Map<String, List<CompanyCoverRateInfoResponse>> tmpCompanyIdCoverRateResponse = new HashMap<>();
        if (ObjectUtils.isEmpty(cycleNumCompanyCoverageMap)) {
            all.forEach(item -> cycleNumList.forEach(cycleNum -> {
                List<CompanyCoverRateInfoResponse> res = refreshCompanyCoverRates(item.getId(), cycleNum);
                Map<String, List<CompanyCoverRateInfoResponse>> map = cycleNumCompanyCoverageMap.getIfPresent(cycleNum);
                if (map == null) {
                    map = new ConcurrentHashMap<>();
                }
                map.put(item.getId(), res);
                cycleNumCompanyCoverageMap.put(cycleNum, map);
            }));
        } else {
            // 如果不空, 更新当前周期的数据
            all.forEach(item -> {
                List<CompanyCoverRateInfoResponse> companyCoverRates = refreshCompanyCoverRates(item.getId(), currentCycleNum);
                Map<String, List<CompanyCoverRateInfoResponse>> stringListMap = cycleNumCompanyCoverageMap.getIfPresent(currentCycleNum);
                if (stringListMap == null) {
                    stringListMap = new ConcurrentHashMap<>();
                }
                stringListMap.put(item.getId(), companyCoverRates);
                cycleNumCompanyCoverageMap.put(currentCycleNum, stringListMap);
            });
        }
//        companyIdCoverRateResponseMap = tmpCompanyIdCoverRateResponse;
        log.debug("道路覆盖率缓存完成...");
    }

    public List<CoverRateAll> getCoverRateTotal(Set<String> companySet) {
        if (MapUtils.isEmpty(coverRateMap)) {
            refreshCoverRate();
        }
        return coverRateMap.values().stream().filter(item -> companySet.contains(item.getCompany())).collect(Collectors.toList());
    }

    /**
     * @param companyId 公司id
     * @return
     */
    public CoverRateAll getCoverRateByCompanyId(String companyId) {
        if (MapUtils.isEmpty(coverRateMap)) {
            refreshCoverRate();
        }
        return coverRateMap.get(companyId);
    }

    @Scheduled(cron = "0 0/10 6-18 * * ?", zone = "GMT+8:00")
    public void refreshCoverRate() {
        Set<String> companyIdSet = companyKeyPointsMap.keySet();
        Query queryCoverage = new Query();
        //初始化结果集
        List<CoverRateAll> coverRateAllList = new ArrayList<>();
        companyIdSet.forEach(companyId -> {
            CoverRateAll coverRateAll = CoverRateAll
                    .builder().company(companyId).todayRate(0d).lastWeekRate(0d)
                    .weekRate(0d).monthRate(0d).lastMonthRate(0d).build();
            coverRateAllList.add(coverRateAll);
        });
        //计算时间戳
        Long startOfDay = MyDateUtil.getStartOfDay(new Date()).getTime() / 1000;
        Long mondayOfWeek = MyDateUtil.getMondayOfWeek(new Date()).getTime() / 1000;
        Long mondayOfLastWeek = MyDateUtil.getMondayOfLastWeek().getTime() / 1000;
        Long firstDayOfMonth = MyDateUtil.getFirstDayOfMonth().getTime() / 1000;
        Long firstDayOfLastMonth = MyDateUtil.getFirstDayOfLastMonth().getTime() / 1000;

        queryCoverage.addCriteria(Criteria.where("timestamp").gte(firstDayOfLastMonth));
        //待筛选数据
        List<CarCoverage> coverageList = carCoverageDao.findByQuery(queryCoverage);
        //上月覆盖率表达式
        Predicate<CarCoverage> lastMonthPredicate = carCoverage -> carCoverage.getTimestamp() < firstDayOfMonth;
        List<CoverRateAll> lastMonthCoverRateAll = convertCarCoverage(lastMonthPredicate, coverageList, coverRateAllList, CoverageTypeEnum.LAST_MONTH_RATE);
        //本月覆盖率表达式
        Predicate<CarCoverage> monthPredicate = carCoverage -> carCoverage.getTimestamp() > firstDayOfMonth;
        List<CoverRateAll> monthCoverRateAll = convertCarCoverage(monthPredicate, coverageList, lastMonthCoverRateAll, CoverageTypeEnum.MONTH_RATE);
        //上周覆盖率表达式
        Predicate<CarCoverage> lastWeekPredicate = carCoverage -> carCoverage.getTimestamp() > mondayOfLastWeek && carCoverage.getTimestamp() < mondayOfWeek;
        List<CoverRateAll> lastWeekCoverRateAll = convertCarCoverage(lastWeekPredicate, coverageList, monthCoverRateAll, CoverageTypeEnum.LAST_WEEK_RATE);
        //本周覆盖率表达式
        Predicate<CarCoverage> weekPredicate = carCoverage -> carCoverage.getTimestamp() > mondayOfWeek;
        List<CoverRateAll> weekCoveRateAll = convertCarCoverage(weekPredicate, coverageList, lastWeekCoverRateAll, CoverageTypeEnum.WEEK_RATE);
        //今日覆盖率表达式
        Predicate<CarCoverage> todayPredicate = carCoverage -> carCoverage.getTimestamp() > startOfDay;
        coverRateMap = convertCarCoverage(todayPredicate, coverageList, weekCoveRateAll, CoverageTypeEnum.TODAY_RATE).stream().collect(Collectors.toMap(CoverRateAll::getCompany, item -> item));
    }

    public List<CoverRateAll> convertCarCoverage(Predicate<CarCoverage> predicate, List<CarCoverage> coverageList, List<CoverRateAll> coverRateAllList, CoverageTypeEnum coverageTypeEnum) {
        //根据养护公司分组
        Map<String, List<CarCoverage>> map = coverageList.stream().filter(predicate).collect(Collectors.groupingBy(CarCoverage::getCompanyId));
        map.forEach((companyId, carCoverages) -> {
            //取并集并去重(可能为空)
            Set<String> coverPointSet = carCoverages.stream().map(CarCoverage::getCoverPoint).collect(Collectors.toSet());
            double coverPointSetSize = coverPointSet.size();
//            log.info(companyId + "已覆盖点位集合个数：" + coverPointSetSize);
            Map<String, Set<String>> keyPointSet = companyKeyPointsMap.get(companyId);
            //有的公司关键点位可能为空
            if (MapUtils.isEmpty(keyPointSet)) {
                return;
            }
            double keyPointSetSize = keyPointSet.size();
//            log.info(companyId + "关键点位集合个数：" + keyPointSetSize);
            //计算覆盖率
            double coverage = coverPointSetSize / keyPointSetSize;
            String coverageStr = String.format("%.4f", coverage);
            coverage = Double.parseDouble(coverageStr);
            Optional<CoverRateAll> optional = coverRateAllList.stream().filter(c -> c.getCompany().equals(companyId)).findFirst();
            if (optional.isPresent()) {
                CoverRateAll coverRateAll = optional.get();
                switch (coverageTypeEnum) {
                    case TODAY_RATE:
                        coverRateAll.setTodayRate(coverage);
                        break;
                    case WEEK_RATE:
                        coverRateAll.setWeekRate(coverage);
                        break;
                    case LAST_WEEK_RATE:
                        coverRateAll.setLastWeekRate(coverage);
                        break;
                    case MONTH_RATE:
                        coverRateAll.setMonthRate(coverage);
                        break;
                    case LAST_MONTH_RATE:
                        coverRateAll.setLastMonthRate(coverage);
                        break;
                    default:
                }
            } else {
                throw new IllegalArgumentException("数据库数据错误！");
            }
        });
        return coverRateAllList;
    }

    public List<CompanyCoverRateInfoResponse> getCompanyCoverRates(String companyId, String cycleNum) {
        Map<String, List<CompanyCoverRateInfoResponse>> companyCoverageResp = cycleNumCompanyCoverageMap.getIfPresent(cycleNum);
        if (ObjectUtils.isNotEmpty(companyCoverageResp) && companyCoverageResp.containsKey(companyId)) {
            return companyCoverageResp.get(companyId);
        }
        List<CompanyCoverRateInfoResponse> res = refreshCompanyCoverRates(companyId, cycleNum);
        Map<String, List<CompanyCoverRateInfoResponse>> map = cycleNumCompanyCoverageMap.getIfPresent(cycleNum);
        if (map == null) {
            map = new ConcurrentHashMap<>();
        }
        map.put(companyId, res);
        cycleNumCompanyCoverageMap.put(cycleNum, map);
        return res;
    }

    public List<CompanyCoverRateInfoResponse> refreshCompanyCoverRates(String companyId, String cycleNum) {
        String yearMonth = MyDateUtil.dateFormatStr(MyDateUtil.getFirstDayOfLastMonth(), "yyyy-MM");
        Stopwatch stopwatch = Stopwatch.createStarted();
        Company company = companyDao.fetch(companyId);
        Set<String> unitIdList = company.getUnits();
        List<UnitDensity> unitDensityList = unitDensityDao.findByQuery(new Query(Criteria.where("cycle_num").is(cycleNum))).stream().filter(item -> item.getUnitId() != null).collect(Collectors.toList());
        Map<String, List<UnitDensity>> unitIdDensityGroupMap = unitDensityList.stream().collect(Collectors.groupingBy(UnitDensity::getUnitId));
        new Query(Criteria.where("coordinates").exists(true));
        Map<String, List<RoadInfo>> unitIdRoadMap = roadInfoDao.findAll()
                .stream()
                .filter(item -> ObjectUtils.isNotEmpty(item.getUnitId()))
                .collect(Collectors.groupingBy(RoadInfo::getUnitId));
        List<UnitCoverageWeek> unitCoverageWeekList = unitCoverageWeekDao.findByQuery(new Query(Criteria.where("cycle_num").is(cycleNum)));
        List<RoadCoverageWeek> roadCoverageWeekList = roadCoverageWeekDao.findByQuery(new Query(Criteria.where("cycle_num").is(cycleNum)));
        Map<String, UnitCoverageWeek> unitCoverageMap = unitCoverageWeekList.stream().filter(item -> item.getUnitId() != null).collect(Collectors.toMap(UnitCoverageWeek::getUnitId, item -> item, (identity1, identity2) -> identity1));
        Map<String, RoadCoverageWeek> roadCoverageWeekMap = roadCoverageWeekList.stream().collect(Collectors.toMap(id -> id.getUnitId() + "_" + id.getRoadId(), item -> item));
        Map<String, Double> roadIdCoverageMap = roadCoverageWeekList.stream().collect(Collectors.toMap(RoadCoverageWeek::getRoadId, RoadCoverageWeek::getCycleCoverage, (density1, density2) -> density1));
        List<CompanyCoverRateInfoResponse> res = unitIdList.stream().map(unitId -> {
                    CompanyCoverRateInfoResponse companyCoverRateInfoResponse = new CompanyCoverRateInfoResponse();
                    Unit unit = unitDao.fetch(unitId);
                    if (unit != null) {
                        processUnitScore(companyCoverRateInfoResponse, unitId, yearMonth);
                        companyCoverRateInfoResponse.setUnitName(unit.getUnitName());
                        companyCoverRateInfoResponse.setUnitId(unit.getId());
                        companyCoverRateInfoResponse.setRoadType(unit.getRoadType());
                        if (unitCoverageMap.containsKey(unitId)) {
                            UnitCoverageWeek unitCoverageWeek = unitCoverageMap.get(unitId);
                            companyCoverRateInfoResponse.setRealCover(unitCoverageWeek.getRealCover() + "");
                            companyCoverRateInfoResponse.setShouldCover(unitCoverageWeek.getShouldCover() + "");
                            companyCoverRateInfoResponse.setWeekRate(unitCoverageWeek.getCycleCoverage() + "");
                        }
                        if (unitIdDensityGroupMap.containsKey(unitId)) {
                            List<UnitDensity> unitDensities = unitIdDensityGroupMap.get(unitId);
//                    double density = unitDensities.stream().mapToDouble(UnitDensity::getDensity).sum();
                            int count = unitDensities.stream().mapToInt(UnitDensity::getCount).sum();
                            companyCoverRateInfoResponse.setDiseaseCount(count + "");
                        } else {
                            companyCoverRateInfoResponse.setDiseaseCount("0");
                        }
                        List<RoadCoverDetail> roadCoverDetails = unitIdRoadMap.getOrDefault(unitId, new ArrayList<>()).stream().map(roadInfo -> {
                            RoadCoverDetail roadCoverDetail = new RoadCoverDetail();
                            roadCoverDetail.setCoordinates(roadInfo.getCoordinates());
//                    RoadCoverage roadCoverage = roadCoverageList.stream().filter(item -> item.getRoadId().equals(roadInfo.getSecNum()) && item.getUnitId().equals(roadInfo.getUnitId())).findFirst().get();
                            RoadCoverageWeek roadCoverageWeek = roadCoverageWeekMap.get(unitId + "_" + roadInfo.getSecNum());
//                    roadCoverDetail.setCoverRate(roadIdCoverageMap.get(roadInfo.getSecNum()) + "");
                            String coverage;
                            if (ObjectUtils.isEmpty(roadCoverageWeek)) {
                                // 排查数据
                                log.debug("未获取到中海道路覆盖率, roadId: {}, unitId: {}", roadInfo.getSecNum(), unitId);
                            } else {
                                coverage = roadCoverageWeek.getCycleCoverage() + "";
                                roadCoverDetail.setCoverRate(coverage);
                                roadCoverDetail.setWorkLengthAll(ObjectUtils.isEmpty(roadCoverageWeek.getWorkLengthAll()) ? 0 : roadCoverageWeek.getWorkLengthAll());
                                roadCoverDetail.setWorkLength(ObjectUtils.isEmpty(roadCoverageWeek.getWorkLength()) ? 0 : roadCoverageWeek.getWorkLength());
                                roadCoverDetail.setStatus(roadCoverageWeek.getStatus());
                            }
                            roadCoverDetail.setRoadId(roadInfo.getId());
                            roadCoverDetail.setRoadName(roadInfo.getRoadName());
                            return roadCoverDetail;
                        }).collect(Collectors.toList());
                        companyCoverRateInfoResponse.setRoads(roadCoverDetails);
                        return companyCoverRateInfoResponse;
                    }
                    return null;
                }).filter(Objects::nonNull)
                .sorted(Comparator.comparing(CompanyCoverRateInfoResponse::getUnitId))
                .collect(Collectors.toList());
        stopwatch.stop();
        long elapsed = stopwatch.elapsed(TimeUnit.SECONDS);
        log.debug("统计公司: {}, 周期: {}的道路覆盖率耗时: {}s", companyId, cycleNum, elapsed);
        return res;
    }

    private void processUnitScore(CompanyCoverRateInfoResponse response, String unitId, String yearMonth) {
        UnitScore unitScore = unitScoreDao.findOne(new Query(Criteria.where("unit_id").is(unitId).and("date").is(yearMonth)));
        if (ObjectUtils.isEmpty(unitScore)) {
            return;
        }
        response.setScore(unitScore.getTotal());
    }

    public List<UnitDensityResponse> getUnitDensity() {
        Date today = new Date();
        String endDate = sdfDate.format(today);
        Criteria criteria = Criteria.where("date").is(endDate);
        List<RoadCoverageDensity> roadCoverageDensityList = roadCoverageDensityDao.findByQuery(new Query(criteria));
        Map<String, String> roadIdDensityMap = roadCoverageDensityList.stream().collect(Collectors.toMap(RoadCoverageDensity::getRoadId, RoadCoverageDensity::getDensity));
        Map<String, List<RoadInfo>> unitRoadMap = roadInfoDao.findAll().stream().filter(item -> !item.getRoadType().equals(RoadTypeEnum.ROAD)).collect(Collectors.groupingBy(RoadInfo::getUnitId));

        List<Company> all1 = companyDao.findAll();
        Map<String, String> unitCompanyNameMap = new HashMap<>();
        all1.forEach(item -> {
            for (String unit : item.getUnits()) {
                unitCompanyNameMap.put(unit, item.getName());
            }
        });
        List<Unit> allUnit = unitDao.findAll();
        return allUnit.stream().map(item -> {
            UnitDensityResponse unitDensityResponse = new UnitDensityResponse();
            unitDensityResponse.setUnitName(item.getUnitName());
            unitDensityResponse.setCompany(unitCompanyNameMap.get(item.getId()));
            unitDensityResponse.setRoadType(item.getRoadType());
            List<RoadDensity> roads = unitRoadMap.getOrDefault(item.getId(), new ArrayList<>()).stream().map(
                    one -> {
                        RoadDensity roadDensity = new RoadDensity();
                        roadDensity.setDensity(roadIdDensityMap.get(one.getSecNum()));
                        roadDensity.setSecNum(one.getSecNum());
                        roadDensity.setRoadName(one.getRoadName());
                        roadDensity.setStartStake(one.getStartStake());
                        roadDensity.setEndStake(one.getEndStake());
                        roadDensity.setStartEndPoint(one.getStartEndPoint());
                        roadDensity.setLength(one.getSecLen());
                        return roadDensity;
                    }
            ).collect(Collectors.toList());
            unitDensityResponse.setRoads(roads);
            return unitDensityResponse;
        }).collect(Collectors.toList());
    }

    public List<UnitCoverRateResponse> getUnitCoverage(Set<String> unitIdSet, String currentCycleNum) {
        if (MapUtils.isEmpty(monthlyUnitCoverageMap)) {
            refreshUnitCoverRate();
        }
        String lastCycleNum = cycleNumService.getLastCycleNum(currentCycleNum);
        Map<String, UnitCoverRateResponse> currentUnitCoverageMap = monthlyUnitCoverageMap.get(currentCycleNum);
        if (ObjectUtils.isEmpty(currentUnitCoverageMap)) {
            throw new BadRequestException("非法参数！！！");
        }
        Map<String, UnitCoverRateResponse> lastUnitCoverageMap = lastCycleNum == null ? null : monthlyUnitCoverageMap.get(lastCycleNum);
        currentUnitCoverageMap.forEach((unitId, coverage) -> {
            String lastCycleNumCoverage = "0";
            if (ObjectUtils.isNotEmpty(lastUnitCoverageMap) && ObjectUtils.isNotEmpty(lastUnitCoverageMap.get(unitId))) {
                lastCycleNumCoverage = lastUnitCoverageMap.get(unitId).getWeekRate();
            }
            coverage.setLastWeekRate(lastCycleNumCoverage);
        });
        return currentUnitCoverageMap.values().stream().filter(item -> unitIdSet.contains(item.getUnitId()))
//                .filter(item -> {
//                    String unitId = item.getUnitId();
//                    if (roadCoverUnitIdConfigForHighway.contains(unitId))
//                        return true;
//                    for (String highwayUnitId : roadCoverUnitIdConfigForRoad) {
//                        if(unitId.startsWith(highwayUnitId))
//                            return true;
//                    }
//                    log.debug("道路巡检率模块发现未知标段信息,进行屏蔽,unitId = {}",unitId);
//                    return false;
//                })
                .map(item ->{
                    String unitId = item.getUnitId();
                    String unitName = item.getUnitName();
                    // 公路过滤逻辑
                    if (roadCoverUnitIdConfigForHighway.contains(unitId)) {
                        item.setRoadType(RoadTypeEnum.DIRECT.toString());
                        return item;
                    }
                    // 管理区段需要展示
                    if (unitName.startsWith("管理") && unitName.endsWith("标")) {
                        item.setRoadType(RoadTypeEnum.DIRECT.toString());
                        return item;
                    }
                    // 道路过滤逻辑
                    for (String highwayUnitId : roadCoverUnitIdConfigForRoad) {
                        if(unitId.startsWith(highwayUnitId)) {
                            item.setRoadType(RoadTypeEnum.HIGHWAY.toString());
                            return item;
                        }
                    }
                    log.debug("道路巡检率模块发现未知标段信息,进行屏蔽,unitId = {}",unitId);
                    return null;
                })
                .filter(item -> item != null)
                .sorted(Comparator.comparing(UnitCoverRateResponse::getUnitId))
                .collect(Collectors.toList());
    }

    public List<UncoverInfo> getCoverRateInfo(String companyId) {
        List<UncoverInfo> res = new ArrayList<>();
        Long mondayOfWeek = MyDateUtil.getMondayOfWeek(new Date()).getTime() / 1000;
        Query query = new Query();
        //查询本周已覆盖的道路信息
        query.addCriteria(Criteria.where("company_id").is(companyId).and("timestamp").gte(mondayOfWeek));
        List<CarCoverage> coverageList = carCoverageDao.findByQuery(query);
        Set<String> coverPointSet = coverageList.stream().map(CarCoverage::getCoverPoint).collect(Collectors.toSet());
        Set<String> gpsKeyPointSet = companyKeyPointsMap.get(companyId).keySet();
        Set<String> copySet = new HashSet<>(gpsKeyPointSet);
        //有的公司可能还没有关键点位
        if (ObjectUtils.isEmpty(gpsKeyPointSet)) {
            return res;
        }
        //找出未覆盖的关键点位(差集)
        copySet.removeAll(coverPointSet);
        Set<String> keyPointsIds = copySet.stream().map(item -> item + "_" + companyId).collect(Collectors.toSet());
        //可能覆盖率为百分百
        if (ObjectUtils.isEmpty(keyPointsIds)) {
            return res;
        }
        List<GpsKeyPoint> gpsKeyPointList = gpsKeyPointsDao.findByQuery(new Query(Criteria.where("_id").in(keyPointsIds)));
        gpsKeyPointList.forEach(points -> {
            String roadName = StringUtils.isEmpty(points.getRoadName()) ? "" : points.getRoadName();
            UncoverInfo uncoverInfo = UncoverInfo.builder().roadName(roadName).gpsLocation(points.getGpsLocation()).build();
            res.add(uncoverInfo);
        });
        return res;
    }

    public void updateCompanyInfo2() {
        List<GpsKeyPoint> gpsKeyPointList = gpsKeyPointsDao.findAll();
        Map<String, List<GpsKeyPoint>> map = gpsKeyPointList.stream().collect(Collectors.groupingBy(GpsKeyPoint::getCompanyId));
        List<Company> companyList = companyDao.findAll();
        companyList.forEach(company -> {
            List<GpsKeyPoint> keyPointsList = map.get(company.getId());
            if (ObjectUtils.isNotEmpty(keyPointsList)) {
                Set<String> newKeyPoints = keyPointsList.stream().map(GpsKeyPoint::getKeyPoint).collect(Collectors.toSet());
                if (company.getKeyPoints().size() != newKeyPoints.size()) {
                    company.setKeyPoints(newKeyPoints);
                    companyDao.saveOrUpdate(company);
                }

            }
        });
    }

    public void updateCompanyKeyPointsGps() {
        List<GpsKeyPoint> gpsKeyPointList = gpsKeyPointsDao.findAll();
        Set<String> set = gpsKeyPointList.stream().map(GpsKeyPoint::getCompanyId).collect(Collectors.toSet());
        //剩余待转换的gps坐标
        List<GpsTest> all = gpsTestDao.findByQuery(new Query(Criteria.where("company_id").nin(set)));
        all.parallelStream().forEach(gpsTest -> {
            String gpsLocations = gpsTest.getGpsLocations();
            gpsLocations = gpsLocations.substring(1, gpsLocations.length() - 1);
            String regex = "\\[(.*?)]";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(gpsLocations);
            while (matcher.find()) {
                String locationStr = matcher.group(1);
                String[] strings = locationStr.split(", ");
                GpsLocation gpsLocation = GpsLocation.builder().longitude(Double.parseDouble(strings[0])).latitude(Double.parseDouble(strings[1])).build();
                String keyPoint = gpsToString(gpsLocation);
//                String streetName = baiduMapClient.geocoder(gpsLocation);
                GpsKeyPoint gpsKeyPoint = GpsKeyPoint.builder().companyId(gpsTest.getCompanyId()).gpsLocation(gpsLocation).keyPoint(keyPoint).build();
                gpsKeyPointsDao.saveOrUpdate(gpsKeyPoint);
            }
        });
    }

    public void updateGpsKeyPointsRoadName() {
        AtomicInteger atomicInteger = new AtomicInteger();
        List<GpsKeyPoint> gpsKeyPointList = gpsKeyPointsDao.findByQuery(new Query(Criteria.where("road_name").is(null)));
        gpsKeyPointList.parallelStream().forEach(gpsKeyPoint -> {
            if (gpsKeyPoint.getRoadName().length() > 7) {
                return;
            }
//            long start = System.currentTimeMillis();
//            String roadInfo = zhonghaiClient.getRoadInfo(gpsKeyPoints.getGpsLocation());
//            log.info("获取道路信息耗时 {} ms", System.currentTimeMillis() - start);
//            //查询到的道路信息可能是空的
//            if (ObjectUtils.isEmpty(roadInfo)) {
//                int i = atomicInteger.incrementAndGet();
//                log.info("中海查询不到的坐标个数： {}", i);
//                return;
//            }


            String roadInfo = baiduMapClient.geocoder(gpsKeyPoint.getGpsLocation());
            if (ObjectUtils.isEmpty(roadInfo)) {
                int i = atomicInteger.incrementAndGet();
                log.info("百度查询不到的坐标个数： {}", i);
                return;
            }


            gpsKeyPoint.setRoadName(roadInfo);
            gpsKeyPointsDao.saveOrUpdate(gpsKeyPoint);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    public String findCompanyIdByUnitId(String unitId) {
        Company company = companyDao.findOne(new Query(Criteria.where("units").is(unitId)));
        return company.getId();
    }

    public void refreshRoadCoverageWeekByFix(String unitId, String roadId) {
        // 更新road_coverage_week的status
        String currentCycleNum = cycleNumService.getCurrentCycleNum();
        Criteria criteria = Criteria.where("unit_id").is(unitId)
                .and("cycle_num").is(currentCycleNum);
        if (StringUtils.isNotEmpty(roadId)) {
            String[] s = roadId.split("_");
            criteria.and("road_id").is(s[1]);
        }
        Query query = new Query(criteria);
        List<RoadCoverageWeek> roadCoverageWeekList = roadCoverageWeekDao.findByQuery(query);
        roadCoverageWeekList.forEach(roadCoverageWeek -> {
            roadCoverageWeek.setStatus(RoadCoverDetail.RoadCoverageStatusEnum.AMENDMENT);
            roadCoverageWeekDao.saveOrUpdate(roadCoverageWeek);
        });
        // 更新缓存
        String companyId = findCompanyIdByUnitId(unitId);
        List<CompanyCoverRateInfoResponse> refresh = refreshCompanyCoverRates(companyId, currentCycleNum);
        Map<String, List<CompanyCoverRateInfoResponse>> map = cycleNumCompanyCoverageMap.getIfPresent(currentCycleNum);
        if (map == null) {
            map = new ConcurrentHashMap<>();
        }
        map.put(companyId, refresh);
        cycleNumCompanyCoverageMap.put(currentCycleNum, map);
    }

    public String gpsToString(GpsLocation gpsLocation) {
        return ((Double) (gpsLocation.getLongitude() * 1000)).intValue() + "_" + ((Double) (gpsLocation.getLatitude() * 1000)).intValue();
    }
//    @Scheduled(cron = "0/5 * * * * ?")
//    public void flushData() {
//        List<RoadCoverageWeek> all = roadCoverageWeekDao.findAll();
//        all.parallelStream().forEach(item -> {
//            if (com.google.common.base.Objects.equal(item.getStatus(), RoadCoverDetail.RoadCoverageStatusEnum.AMENDMENT)) {
//                return;
//            }
//            item.setStatus(RoadCoverDetail.RoadCoverageStatusEnum.UNCOVER);
//            roadCoverageWeekDao.saveOrUpdate(item);
//        });
//    }
}
